A typical data storage system includes data storage circuitry and a power subsystem. The data storage circuitry (e.g., one or more storage processors, one or more directors, etc.) is configured to perform data storage operations on behalf of external hosts. The power subsystem is configured to provide power to the data storage circuitry, as well as monitor the operating environment to ensure safe and reliable operation of the data storage circuitry.
Due to the complex functionality and performance demands imposed on the power subsystem, many manufacturers equip the power subsystem with a dedicated microprocessor which is separate from the processors of the data storage circuitry. That is, this dedicated microprocessor is not configured to store data into and retrieve data from an array of storage devices using a preemptive scheduling operating system and specialized data storage processes as is the data storage circuitry. Rather, the dedicated microprocessor of the power subsystem executes a control program having (i) a startup portion that directs the power subsystem to provide power supply signals to the data storage circuitry in a proper sequence to ensure correct operation, and (ii) a normal operating portion to monitor the operating environment and notify the data storage circuitry if there is a sensed problem in the operating environment.
As the dedicated microprocessor of the power subsystem executes the normal operating portion of the control program, the dedicated microprocessor cycles through a variety of monitoring routines. In general, each monitoring routine directs the microprocessor to sample a signal from a sensor (e.g., an airflow sensor, a temperature sensor, a voltage sensor, etc.), and output a warning to the data storage circuitry if the sampled signal indicates that the operating environment is no longer within a prescribed operating range (e.g., if the airflow sensor indicates that airflow passing over the data storage circuitry is no longer above a predetermined threshold, if the temperature sensor indicates that the temperature of the operating environment exceeds the temperature margin of the data storage circuitry, if the voltage sensor indicates that the primary power signal is outside a safe operating range, etc.).
Since the dedicated microprocessor of the power subsystem is separate from the data storage circuitry and thus is not burdened with the task of performing data storage operations, the dedicated microprocessor typically executes the control program without the use of an overhead-intensive preemptive scheduling operating system thus avoiding the associated performance costs of such an operating system. Rather, as the dedicated microprocessor runs the control program, the control program itself task switches between the different monitoring routines in a round robin manner. That is, the microprocessor runs a segment of a first monitoring routine, then stores an address of a next segment of the first monitoring routine and jumps to a second monitoring routine, then runs a segment of the second monitoring routine, then stores an address of a next segment of the second monitoring routine and jumps to a segment of a third monitoring routine, and so on. Once the dedicated microprocessor has cycled through a segment of each routine, the microprocessor runs the next segment of each routine in the same manner. Such light-weight multitasking results in fast performance and efficient use of resources (i.e., processing time, memory, etc.).
To create the above-described control program, a programmer manually embeds labels and procedure calls into the program's source code. In particular, at the beginning of each code segment in each monitoring routine, the programmer places a unique label for entering that code segment. Additionally, at the end of each code segment, the programmer places a procedure call for storing the address of the next segment of the current routine (i.e., an entry point), exiting that routine and jumping to the next routine. The procedure calls reference the unique labels as parameters in order to remember where to continue execution of each routine when it is time to continue execution of that routine.
For example, the following monitoring routine is configured to continuously perform nine instructions arranged as a series of three code segments.
go_to_code_segment(x);while (1) {entry_point_1:instruction_1;instruction_2;instruction_3;jump_to_next_monitoring_routine(entry_point_2);entry_point_2:instruction_4;instruction_5;instruction_6;jump_to_next_monitoring_routine(entry_point_3);entry_point_3:instruction_7;instruction_8;instruction_9;jump_to_next_monitoring_routine(entry_point_1);};The first code segment, which starts at address “entry_point—1” (i.e., “entry_point—1” is an entry point into the first segment), includes “instruction—1”, “instruction—2”, “instruction—3”, and “jump_to_next_monitoring_routine(entry_point—2)”. The second code segment, which starts at address “entry_point—2”, includes “instruction—4”, “instruction—5”, “instruction—6”, and “jump_to_next_monitoring_routine(entry_point—3)”. The third code segment, which starts at address “entry_point—3”, includes “instruction—7”, “instruction—8”, “instruction—9”, and “jump_to_next_monitoring_routine(entry_point—1)”.
After the programmer compiles and links the source code, the control program is in executable form and ready to run on the dedicated microprocessor of the power subsystem. During execution, the microprocessor executes the procedure call “go_to_code_segment” which returns immediately during the initial execution but otherwise causes the microprocessor to jump to a particular code segment within the “while” loop. During the initial execution, the microprocessor then enters the “while” loop, and executes “instruction—1”, “instruction—2”, and “instruction—3”. After the microprocessor executes “instruction—3”, the microprocessor runs the procedure call “jump_to_next_monitoring_routine(entry_point—2)” which causes the microprocessor to (i) save the address “entry_point—2” in memory (i.e., “entry_point—2” is the starting point of the next segment in the current monitoring routine), (ii) exit the current monitoring routine and (ii) jump to the next monitoring routine. The microprocessor then runs a segment of each remaining monitoring routine in turn and eventually executes a procedure call which directs the microprocessor to run the current monitoring routine again. That is, the microprocessor (which had earlier saved the address “entry_point—2” in memory) re-runs the procedure call “go_to_code_segment” which now causes the microprocessor to re-enter the current monitoring routine at the address “entry_point 2” and run the next segment of code, i.e., “instruction—4”, “instruction—5”, “instruction—6”, and “jump_to_next_monitoring_routine(entry_point—3)”. The microprocessor progresses through the next segments of the remaining monitoring routines in the same manner. Accordingly, the above-described control program efficiently and effectively carries out very light-weight task switching without the use of an overhead-intensive preemptive scheduling operating system.